/*
 * Exploder
 * 
 * Tero Pyylampi (218437), Jussi Tuurinkoski (211594)
 *
 * Level Editor layer
 */

#include "EditorLayer.h"
#include "EditorScene.h"
#include "EditorLayerInterface.h"
#include "../Common.h"
#include "../Resources.h"
#include "../game/GameCamera.h"
#include "../game/misc/GameTouchHandle.h"
#include "../game/objects/physicobjects/GamePhysicObjectEngine.h"
#include "../game/objects/physicobjects/GamePhysicObject.h"
#include "../game/objects/physicobjects/elements/GameConstructionElement.h"
#include "../game/interface/GameInterfaceItemAndObjectContainer.h"
#include "../game/interface/GameInterfaceEditorContainer.h"
#include "../game/interface/GameInterfaceEngine.h"
#include "../game/interface/GameInterfacePhysicObjectButton.h"

// default constructor
EditorLayer::EditorLayer( EditorScene *scene ) : mEditorScene( scene ), mPhysicsWorld( 0 ), mTouchHandle( 0 ), mActiveElement( 0 ) {
	initializeLayer();
}

// default destructor
EditorLayer::~EditorLayer() {
	for( unsigned int i = 0; i < mEngines->size(); ++i )
		delete mEngines->at( i );
	delete mEngines;
}

// initialize this scene
void EditorLayer::initializeLayer() {
	// start new level
	initializeNewLevel();
	// init touch handle
	b2BodyDef groundBodyDef;
	b2Body *groundBody = mPhysicsWorld->CreateBody( &groundBodyDef );
	mTouchHandle = new GameTouchHandle( mEditorScene->getGameCamera(), groundBody );
	// allow input
	setIsTouchEnabled( true );
}

// start building a new level
void EditorLayer::initializeNewLevel() {
	// remove old physics world if necessary
	if( mPhysicsWorld != 0 )
		delete mPhysicsWorld;

	// create a new world
	b2Vec2 gravity( 0, -9.8f );
	mPhysicsWorld = new b2World( gravity );
	mPhysicsWorld->SetAllowSleeping( true );
	mPhysicsWorld->SetContinuousPhysics( true );

	// set default world dimensions
	setWorldDimensions( 100, 30 );

	// create different engines
	mEngines = new std::vector< GameAbstractEngine* >();
	GamePhysicObjectEngine *physicEngine = new GamePhysicObjectEngine();
	mEngines->push_back( physicEngine );

	// create ground
	physicEngine->createGroundObject( mPhysicsWorld, this );
}

// update -function
void EditorLayer::update( cocos2d::ccTime dt ) {
	// update world
	if( mPhysicsWorld != 0 ) 
		mPhysicsWorld->Step( dt, BOX2D_VELOCITY_ITERATIONS, BOX2D_POSITION_ITERATIONS );

	// update different objects
	for( unsigned int i = 0; i < mEngines->size(); ++i )
		mEngines->at( i )->update( dt );
}

// called when the user puts the finger on the screen
void EditorLayer::ccTouchesBegan( cocos2d::CCSet *touches, cocos2d::CCEvent *event ) {
	cocos2d::CCTouch *touch = dynamic_cast< cocos2d::CCTouch* >( touches->anyObject() );

	// convert to screen coordinates
	cocos2d::CCPoint pos = touch->locationInView();
	pos = cocos2d::CCDirector::sharedDirector()->convertToGL( pos );
	// get world coordinates
	cocos2d::CCPoint absoluteScreenPos = this->convertToNodeSpace( pos );
	b2Vec2 worldLoc = mEditorScene->getGameCamera()->convertScreenToWorld( b2Vec2( absoluteScreenPos.x, absoluteScreenPos.y ) );

	// check for interface button presses
	b2Body *resultBody = checkForInterfacePhysicObjectButtonPresses( pos, worldLoc );
	if( resultBody != 0 ) {
		mTouchHandle->startObjectTouch( touch, this, resultBody, mPhysicsWorld );
		if( mActiveElement != 0 )
			mActiveElement->setObjectActive( false );
		mActiveElement = static_cast< GameConstructionElement* >( resultBody->GetUserData() );
		mActiveElement->setObjectActive( true );
		return;
	}
	
	// check for existing objects
	GameConstructionElement *object = checkForExistingElements( worldLoc );
	if( object != 0 ) {
		mTouchHandle->startObjectTouch( touch, this, object->getBodyObjects()->at( 0 ), mPhysicsWorld );
		if( mActiveElement != 0 )
			mActiveElement->setObjectActive( false );
		mActiveElement = object;
		mActiveElement->setObjectActive( true );
		return;
	}
	
	// nothing found, start camera touch
	mTouchHandle->startMovingCamera( touch, this );
	if( mActiveElement != 0 )
		mActiveElement->setObjectActive( false );
	mActiveElement = 0;

}

// called when the finger moves on the screen
void EditorLayer::ccTouchesMoved( cocos2d::CCSet *touches, cocos2d::CCEvent *event ) {
	cocos2d::CCTouch *touch = dynamic_cast< cocos2d::CCTouch* >( touches->anyObject() );
	mTouchHandle->update( touch, this );
}

// called when the user lifts the finger from the screen
void EditorLayer::ccTouchesEnded( cocos2d::CCSet *touches, cocos2d::CCEvent *event ) {
	cocos2d::CCTouch *touch = dynamic_cast< cocos2d::CCTouch* >( touches->anyObject() );
	mTouchHandle->reset();
}
	
// get master
EditorScene *EditorLayer::getEditorScene() {
	return mEditorScene;
}

// get physics engine
b2World *EditorLayer::getPhysicsEngine() {
	return mPhysicsWorld;
}

// get world dimensions
b2Vec2 *EditorLayer::getWorldDimensions() {
	return &mWorldDimensions;
}
// set world dimensions
void EditorLayer::setWorldDimensions( float width, float height ) {
	mWorldDimensions.Set( width, height );
}

// get physic object engine
GamePhysicObjectEngine *EditorLayer::getPhysicObjectEngine() {
	return dynamic_cast< GamePhysicObjectEngine* >( mEngines->at( 0 ) );
}

// reset currently active object
void EditorLayer::resetCurrentlyActiveObject() {
	mActiveElement = 0;
}

// check for interface physic object button presses
b2Body *EditorLayer::checkForInterfacePhysicObjectButtonPresses( cocos2d::CCPoint &screenCoords, b2Vec2 &worldCoords ) {
	std::vector< GameInterfacePhysicObjectButton *> container = mEditorScene->getInterfaceLayer()->getInterfaceEngine()->getEditorContainer()->getObjectButtons();
	for( unsigned int i = 0; i < container.size(); ++i )
		if( container.at( i )->testIfCoordinatesAreInsideBackgroundSprite( screenCoords ) )
			return container.at( i )->createAndInitializeNewCorrespondingItem( mEditorScene->getGameCamera(), worldCoords );

	// nothing found
	return 0;
}

// check for existing bodies
GameConstructionElement *EditorLayer::checkForExistingElements( b2Vec2 &worldCoords ) {
	GamePhysicObject *nearestObject = getPhysicObjectEngine()->findNearestPhysicObject( worldCoords );
	if( nearestObject != 0 && nearestObject->calculateDistanceTo( worldCoords ) < 3.0f )
		return dynamic_cast< GameConstructionElement* >( nearestObject );

	// nothing found
	return 0;
}
